<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Eval Explorer</title>
        <style>
            :root {
                /* Light theme (default) */
                --bg-color: #ffffff;
                --text-color: #333333;
                --header-bg: #f8f8f8;
                --border-color: #eaeaea;
                --code-bg: #f5f5f5;
                --link-color: #0066cc;
                --button-bg: #f5f5f5;
                --button-border: #ddd;
                --button-active-bg: #0066cc;
                --button-active-color: white;
                --button-active-border: #0055aa;
                --preview-bg: #f5f5f5;
                --table-line: #f0f0f0;
            }

            /* Dark theme */
            [data-theme="dark"] {
                --bg-color: #1e1e1e;
                --text-color: #e0e0e0;
                --header-bg: #2d2d2d;
                --border-color: #444444;
                --code-bg: #2a2a2a;
                --link-color: #4da6ff;
                --button-bg: #333333;
                --button-border: #555555;
                --button-active-bg: #0066cc;
                --button-active-color: white;
                --button-active-border: #0055aa;
                --preview-bg: #2a2a2a;
                --table-line: #333333;
            }

            /* Apply theme variables */
            body {
                font-family: monospace;
                line-height: 1.6;
                margin: 0;
                padding: 20px;
                color: var(--text-color);
                max-width: 1200px;
                margin: 0 auto;
                background-color: var(--bg-color);
            }
            h1 {
                margin-bottom: 20px;
                border-bottom: 1px solid var(--border-color);
                padding-bottom: 10px;
                font-family: monospace;
            }
            table {
                width: 100%;
                border-collapse: collapse;
                margin-bottom: 20px;
                table-layout: fixed; /* Ensure fixed width columns */
            }
            th,
            td {
                padding: 10px;
                text-align: left;
                border-bottom: 1px dotted var(--border-color);
                vertical-align: top;
                word-wrap: break-word; /* Ensure long content wraps */
                overflow-wrap: break-word;
            }
            th {
                background-color: var(--header-bg);
                font-weight: 600;
            }
            .collapsible {
                cursor: pointer;
                color: var(--link-color);
                text-decoration: underline;
            }
            .hidden {
                display: none;
            }
            .tool-name {
                font-weight: bold;
            }
            .tool-params {
                padding-left: 20px;
                color: #666;
            }
            pre {
                background-color: var(--code-bg);
                padding: 10px;
                border-radius: 5px;
                overflow-x: auto;
                max-height: 200px;
                margin: 10px 0;
                font-family: monospace;
                width: 100%;
                box-sizing: border-box;
                white-space: pre-wrap; /* Ensure text wraps */
                color: var(--text-color);
            }
            code {
                font-family: monospace;
            }

            /* Column sizing */
            .turn-column {
                width: 3%;
                max-width: 3%;
            }
            .text-column {
                width: 22%;
                max-width: 22%;
            }
            .tool-column {
                width: 38%;
                max-width: 38%;
            }
            .result-column {
                width: 37%;
                max-width: 37%;
                overflow-x: auto;
            }

            /* Content formatting */
            .text-content {
                font-family:
                    system-ui,
                    -apple-system,
                    BlinkMacSystemFont,
                    "Segoe UI",
                    Roboto,
                    Oxygen,
                    Ubuntu,
                    Cantarell,
                    "Open Sans",
                    "Helvetica Neue",
                    sans-serif;
                font-size: 0.7rem;
            }
            .action-container .action-preview,
            .action-container .action-full {
                margin-bottom: 5px;
            }
            .preview-content {
                white-space: pre-wrap;
                margin-bottom: 5px;
                background-color: var(--preview-bg);
                padding: 10px;
                border-radius: 5px;
                font-family: monospace;
                width: 100%;
                box-sizing: border-box;
                overflow-wrap: break-word;
                color: var(--text-color);
            }
            .show-more {
                color: var(--link-color);
                cursor: pointer;
                text-decoration: none;
                display: block;
                margin-top: 5px;
            }
            .more-inline {
                color: var(--link-color);
                cursor: pointer;
                text-decoration: none;
                display: inline;
                margin-left: 5px;
            }

            /* Compact mode styles */
            .compact-mode td {
                padding: 5px; /* Reduced padding in compact mode */
            }

            .compact-mode .preview-content {
                padding: 2px;
                margin-bottom: 2px;
            }

            .compact-mode pre {
                padding: 5px;
                margin: 5px 0;
                white-space: pre; /* Don't wrap code in compact mode */
                overflow-x: auto; /* Add horizontal scrollbar */
            }

            .compact-mode .result-column pre,
            .compact-mode .result-column .preview-content {
                max-width: 100%;
                overflow-x: auto;
                white-space: pre;
            }

            /* Make action containers more compact */
            .compact-mode .action-container {
                margin-bottom: 2px;
            }

            /* Reduce space between turns */
            .compact-mode tr {
                border-bottom: 1px solid var(--table-line);
            }

            /* Tool params more compact */
            .compact-mode .tool-params {
                padding-left: 10px;
                margin-top: 2px;
            }

            hr {
                margin: 10px 0;
                border: 0;
                height: 1px;
                background-color: var(--border-color);
            }

            /* View switcher */
            .view-switcher {
                display: flex;
                gap: 10px;
                margin-bottom: 20px;
                align-items: center;
            }

            .view-button {
                background-color: var(--button-bg);
                border: 1px solid var(--button-border);
                border-radius: 4px;
                padding: 5px 15px;
                cursor: pointer;
                font-family: monospace;
                font-size: 0.9rem;
                transition: all 0.2s ease;
                color: var(--text-color);
            }

            .view-button:hover {
                background-color: var(--button-border);
            }

            .view-button.active {
                background-color: var(--button-active-bg);
                color: var(--button-active-color);
                border-color: var(--button-active-border);
            }

            /* Navigation bar styles */
            .thread-navigation {
                display: flex;
                align-items: center;
                margin-bottom: 20px;
                padding: 10px 0;
                border-bottom: 1px solid var(--border-color);
            }

            .nav-button {
                background-color: var(--button-bg);
                border: 1px solid var(--button-border);
                border-radius: 4px;
                padding: 5px 15px;
                cursor: pointer;
                font-family: monospace;
                font-size: 0.9rem;
                transition: all 0.2s ease;
                color: var(--text-color);
            }

            .nav-button:hover:not(:disabled) {
                background-color: var(--button-border);
            }

            .nav-button:disabled {
                opacity: 0.5;
                cursor: not-allowed;
            }

            .thread-indicator {
                margin: 0 15px;
                font-size: 1rem;
                flex-grow: 1;
                text-align: center;
            }

            #thread-id {
                font-weight: bold;
            }

            /* Theme switcher */
            .theme-switcher {
                margin-left: auto;
                display: flex;
                align-items: center;
            }

            .theme-button {
                background-color: var(--button-bg);
                border: 1px solid var(--button-border);
                border-radius: 4px;
                padding: 5px 10px;
                cursor: pointer;
                font-size: 0.9rem;
                transition: all 0.2s ease;
                color: var(--text-color);
                display: flex;
                align-items: center;
            }

            .theme-button:hover {
                background-color: var(--button-border);
            }

            .theme-icon {
                margin-right: 5px;
                font-size: 1rem;
            }
        </style>
    </head>
    <body>
        <h1 id="current-filename">Thread Explorer</h1>
        <div class="view-switcher">
            <button id="full-view" class="view-button active" onclick="switchView('full')">Full View</button>
            <button id="compact-view" class="view-button" onclick="switchView('compact')">Compact View</button>
            <button
                id="export-button"
                class="view-button"
                onclick="exportThreadAsJson()"
                title="Export current thread as JSON"
            >
                Export
            </button>
            <div class="theme-switcher">
                <button id="theme-toggle" class="theme-button" onclick="toggleTheme()">
                    <span id="theme-icon" class="theme-icon">☀️</span>
                    <span id="theme-text">Light</span>
                </button>
            </div>
        </div>
        <div class="thread-navigation">
            <button
                id="prev-thread"
                class="nav-button"
                onclick="previousThread()"
                title="Previous thread (Ctrl+←, k, or h)"
                disabled
            >
                &larr; Previous
            </button>
            <div class="thread-indicator">
                Thread <span id="current-thread-index">1</span> of <span id="total-threads">1</span>:
                <span id="thread-id">Default Thread</span>
            </div>
            <button
                id="next-thread"
                class="nav-button"
                onclick="nextThread()"
                title="Next thread (Ctrl+→, j, or l)"
                disabled
            >
                Next &rarr;
            </button>
        </div>
        <table id="thread-table">
            <thead>
                <tr>
                    <th class="turn-column">Turn</th>
                    <th class="text-column">Text</th>
                    <th class="tool-column">Tool</th>
                    <th class="result-column">Result</th>
                </tr>
            </thead>
            <tbody id="thread-body">
                <!-- Content will be filled dynamically -->
            </tbody>
        </table>

        <script>
            // View mode - 'full' or 'compact'
            let viewMode = "full";

            // Theme mode - 'light', 'dark', or 'system'
            let themeMode = localStorage.getItem("theme") || "system";

            // Function to apply theme
            function applyTheme(theme) {
                const themeIcon = document.getElementById("theme-icon");
                const themeText = document.getElementById("theme-text");

                if (theme === "dark") {
                    document.documentElement.setAttribute("data-theme", "dark");
                    themeIcon.textContent = "🌙";
                    themeText.textContent = "Dark";
                } else {
                    document.documentElement.removeAttribute("data-theme");
                    themeIcon.textContent = "☀️";
                    themeText.textContent = "Light";
                }
            }

            // Function to toggle between light and dark themes
            function toggleTheme() {
                // If currently system or light, switch to dark
                if (themeMode === "system") {
                    const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
                    themeMode = systemDark ? "light" : "dark";
                } else {
                    themeMode = themeMode === "light" ? "dark" : "light";
                }

                // Save preference
                localStorage.setItem("theme", themeMode);

                // Apply theme
                applyTheme(themeMode);
            }

            // Initialize theme based on system or saved preference
            function initTheme() {
                if (themeMode === "system") {
                    // Use system preference
                    const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
                    applyTheme(systemDark ? "dark" : "light");

                    // Listen for system theme changes
                    window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
                        if (themeMode === "system") {
                            applyTheme(e.matches ? "dark" : "light");
                        }
                    });
                } else {
                    // Use saved preference
                    applyTheme(themeMode);
                }
            }

            // Function to switch between view modes
            function switchView(mode) {
                viewMode = mode;

                // Update button states
                document.getElementById("full-view").classList.toggle("active", mode === "full");
                document.getElementById("compact-view").classList.toggle("active", mode === "compact");

                // Add or remove compact-mode class on the body
                document.body.classList.toggle("compact-mode", mode === "compact");

                // Re-render the thread with the new view mode
                renderThread();
            }

            // Function to export the current thread as a JSON file
            function exportThreadAsJson() {
                // Clone the thread to avoid modifying the original
                const threadToExport = JSON.parse(JSON.stringify(thread));

                // Create a Blob with the JSON data
                const blob = new Blob([JSON.stringify(threadToExport, null, 2)], { type: "application/json" });

                // Create a download link
                const url = URL.createObjectURL(blob);
                const a = document.createElement("a");
                a.href = url;

                // Generate filename based on thread ID or index
                const filename =
                    threadToExport.thread_id || threadToExport.filename || `thread-${currentThreadIndex + 1}.json`;
                a.download = filename.endsWith(".json") ? filename : `${filename}.json`;

                // Trigger the download
                document.body.appendChild(a);
                a.click();

                // Clean up
                setTimeout(() => {
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                }, 0);
            }
            // Default dummy thread data for preview purposes
            let dummyThread = {
                messages: [
                    {
                        role: "system",
                        content: [{ Text: "System prompt..." }],
                    },
                    {
                        role: "user",
                        content: [{ Text: "Fix the bug: kwargs not passed..." }],
                    },
                    {
                        role: "assistant",
                        content: [
                            { Text: "I'll help you fix that bug." },
                            {
                                ToolUse: {
                                    name: "list_directory",
                                    input: { path: "fastmcp" },
                                    is_input_complete: true,
                                },
                            },
                        ],
                    },
                    {
                        role: "user",
                        content: [
                            {
                                ToolResult: {
                                    tool_name: "list_directory",
                                    is_error: false,
                                    content:
                                        "fastmcp/src\nfastmcp/tests\nfastmcp/README.md\nfastmcp/pyproject.toml\nfastmcp/.gitignore\nfastmcp/setup.py\nfastmcp/examples\nfastmcp/LICENSE",
                                },
                            },
                        ],
                    },
                    {
                        role: "assistant",
                        content: [
                            { Text: "Let's examine the code." },
                            {
                                ToolUse: {
                                    name: "read_file",
                                    input: {
                                        path: "fastmcp/main.py",
                                        start_line: 253,
                                        end_line: 360,
                                    },
                                    is_input_complete: true,
                                },
                            },
                        ],
                    },
                    {
                        role: "user",
                        content: [
                            {
                                ToolResult: {
                                    tool_name: "read_file",
                                    is_error: false,
                                    content:
                                        "def run_application(app, **kwargs):\n    return anyio.run(app, **kwargs)\n\nasync def start_server():\n    # More code...\n    # Multiple lines of code that would be displayed\n    # when clicking on the show more link\n    app = create_app()\n    await run_app(app)\n\ndef main():\n    # Initialize everything\n    anyio.run(start_server)\n    # Even more code here\n    # that would be shown when the user\n    # expands the content",
                                },
                            },
                        ],
                    },
                    {
                        role: "assistant",
                        content: [
                            { Text: "I found the issue." },
                            {
                                ToolUse: {
                                    name: "edit_file",
                                    input: {
                                        path: "fastmcp/core.py",
                                        old_string: "def start_server(app):\n    anyio.run(app)",
                                        new_string: "def start_server(app, **kwargs):\n    anyio.run(app, **kwargs)",
                                        display_description: "Fix kwargs passing to anyio.run",
                                    },
                                    is_input_complete: true,
                                },
                            },
                        ],
                    },
                    {
                        role: "user",
                        content: [
                            {
                                ToolResult: {
                                    tool_name: "edit_file",
                                    is_error: false,
                                    content: "Made edit to fastmcp/core.py",
                                },
                            },
                        ],
                    },
                    {
                        role: "assistant",
                        content: [
                            { Text: "Let's check if there are any errors." },
                            {
                                ToolUse: {
                                    name: "diagnostics",
                                    input: {},
                                    is_input_complete: true,
                                },
                            },
                        ],
                    },
                    {
                        role: "user",
                        content: [
                            {
                                ToolResult: {
                                    tool_name: "diagnostics",
                                    is_error: false,
                                    content: "No errors found",
                                },
                            },
                        ],
                    },
                ],
            };

            // The actual thread data will be injected here when opened by eval
            let threadsData = window.threadsData || { threads: [dummyThread] };

            // Initialize thread variables
            let threads = threadsData.threads;
            let currentThreadIndex = 0;
            let thread = threads[currentThreadIndex];

            // Function to navigate to the previous thread
            function previousThread() {
                if (currentThreadIndex > 0) {
                    currentThreadIndex--;
                    switchToThread(currentThreadIndex);
                }
            }

            // Function to navigate to the next thread
            function nextThread() {
                if (currentThreadIndex < threads.length - 1) {
                    currentThreadIndex++;
                    switchToThread(currentThreadIndex);
                }
            }

            // Function to switch to a specific thread by index
            function switchToThread(index) {
                if (index >= 0 && index < threads.length) {
                    currentThreadIndex = index;
                    thread = threads[currentThreadIndex];
                    updateNavigationButtons();
                    renderThread();
                }
            }

            // Function to update the navigation buttons state
            function updateNavigationButtons() {
                document.getElementById("prev-thread").disabled = currentThreadIndex <= 0;
                document.getElementById("next-thread").disabled = currentThreadIndex >= threads.length - 1;
                document.getElementById("current-thread-index").textContent = currentThreadIndex + 1;
                document.getElementById("total-threads").textContent = threads.length;
            }

            function renderThread() {
                const tbody = document.getElementById("thread-body");
                tbody.innerHTML = ""; // Clear existing content

                // Set thread name if available
                const threadId = thread.thread_id || `Thread ${currentThreadIndex + 1}`;
                document.getElementById("thread-id").textContent = threadId;

                // Set filename in the header if available
                const filename = thread.filename || `Thread ${currentThreadIndex + 1}`;
                document.getElementById("current-filename").textContent = filename;

                // Skip system message
                const nonSystemMessages = thread.messages.filter((msg) => msg.role !== "system");

                let turnNumber = 0;
                processMessages(nonSystemMessages, tbody, turnNumber);
            }

            function processMessages(messages, tbody) {
                let turnNumber = 0;

                for (let i = 0; i < messages.length; i++) {
                    const msg = messages[i];

                    if (isUserQuery(msg)) {
                        // User message starts a new turn
                        turnNumber++;
                        renderUserMessage(msg, turnNumber, tbody);
                    } else if (msg.role === "assistant") {
                        // Each assistant message is one turn
                        turnNumber++;

                        // Collect all text content and tool uses for this turn
                        let assistantText = "";
                        let toolUses = [];

                        // First, collect all text content
                        for (const content of msg.content) {
                            if (content.hasOwnProperty("Text")) {
                                if (assistantText) {
                                    assistantText += "<br><br>" + formatContent(content.Text);
                                } else {
                                    assistantText = formatContent(content.Text);
                                }
                            } else if (content.hasOwnProperty("ToolUse")) {
                                toolUses.push(content.ToolUse);
                            }
                        }

                        // Create a single row for this turn with text and tools
                        const row = document.createElement("tr");
                        row.id = `assistant-turn-${turnNumber}`;

                        // Start with the turn number and assistant text
                        row.innerHTML = `
                            <td class="text-content">${turnNumber}</td>
                            <td class="text-content"><!--Assistant: <br/ -->${assistantText}</td>
                            <td id="tools-${turnNumber}"></td>
                            <td id="results-${turnNumber}"></td>
                        `;

                        tbody.appendChild(row);

                        // Add all tool calls to the tools cell
                        const toolsCell = document.getElementById(`tools-${turnNumber}`);
                        const resultsCell = document.getElementById(`results-${turnNumber}`);

                        // Process all tools and their results
                        for (let j = 0; j < toolUses.length; j++) {
                            const toolUse = toolUses[j];
                            const toolCall = formatToolCall(toolUse.name, toolUse.input);

                            // Add the tool call to the tools cell
                            if (j > 0) toolsCell.innerHTML += "<hr>";
                            toolsCell.innerHTML += toolCall;

                            // Look for corresponding tool result
                            if (hasMatchingToolResult(messages, i, toolUse.name)) {
                                const resultMsg = messages[i + 1];
                                const toolResult = findToolResult(resultMsg, toolUse.name);

                                if (toolResult) {
                                    // Add the result to the results cell
                                    if (j > 0) resultsCell.innerHTML += "<hr>";

                                    // Create a container for the result
                                    const resultDiv = document.createElement("div");
                                    resultDiv.className = "tool-result";

                                    // Format and display the tool result
                                    formatToolResultInline(toolResult.content.Text, resultDiv);
                                    resultsCell.appendChild(resultDiv);

                                    // Skip the result message in the next iteration
                                    if (j === toolUses.length - 1) {
                                        i++;
                                    }
                                }
                            }
                        }
                    } else if (msg.role === "user" && msg.content.some((c) => c.hasOwnProperty("ToolResult"))) {
                        // Skip tool result messages as they are handled with their corresponding tool use
                        continue;
                    }
                }
            }

            function isUserQuery(message) {
                return message.role === "user" && !message.content.some((c) => c.hasOwnProperty("ToolResult"));
            }

            function renderUserMessage(message, turnNumber, tbody) {
                const row = document.createElement("tr");
                row.innerHTML = `
                    <td>${turnNumber}</td>
                    <td class="text-content"><b>[User]:</b><br/> ${formatContent(message.content[0].Text)}</td>
                    <td></td>
                    <td></td>
                `;
                tbody.appendChild(row);
            }

            function hasMatchingToolResult(messages, currentIndex, toolName) {
                return (
                    currentIndex + 1 < messages.length &&
                    messages[currentIndex + 1].role === "user" &&
                    messages[currentIndex + 1].content.some(
                        (c) => c.hasOwnProperty("ToolResult") && c.ToolResult.tool_name === toolName,
                    )
                );
            }

            function findToolResult(resultMessage, toolName) {
                const toolResultContent = resultMessage.content.find(
                    (c) => c.hasOwnProperty("ToolResult") && c.ToolResult.tool_name === toolName,
                );

                return toolResultContent ? toolResultContent.ToolResult : null;
            }
            function formatToolCall(name, input) {
                // In compact mode, format tool calls on a single line
                if (viewMode === "compact") {
                    const params = [];
                    const fullParams = [];

                    // Process all parameters
                    for (const [key, value] of Object.entries(input)) {
                        if (value !== null && value !== undefined) {
                            // Store full parameter for expanded view
                            let fullValue = typeof value === "string" ? `"${value}"` : value;
                            fullParams.push([key, fullValue]);

                            // Abbreviated value for compact view
                            let displayValue = fullValue;
                            if (typeof value === "string" && value.length > 30) {
                                displayValue = `"${value.substring(0, 30)}..."`;
                            }
                            params.push(`${key}=${displayValue}`);
                        }
                    }

                    const paramString = params.join(", ");
                    const fullLine = `<span class="tool-name">${name}</span>(${paramString})`;

                    // If the line is too long, add a [more] link
                    if (fullLine.length > 80 || params.length > 1) {
                        // Create a container with the compact and full views
                        const compactView = `<span class="tool-name">${name}</span>(${params[0]}, <span class="more-inline" onclick="toggleActionVisibility(this)">[...]</span>)`;

                        // For the full view, use the original untruncated values
                        let result = `<span class="tool-name">${name}</span>(`;
                        const formattedParams = fullParams
                            .map((p) => `&nbsp;&nbsp;&nbsp;&nbsp;${p[0]}=${p[1]}`)
                            .join(",<br/>");
                        const fullView = `${result}<br/>${formattedParams}<br/>)`;

                        return `<div class="action-container">
                            <div class="action-preview">${compactView}</div>
                            <div class="action-full hidden">${fullView}</div>
                        </div>`;
                    }

                    return fullLine;
                }

                // Regular (full) view formatting with multiple lines
                let result = `<span class="tool-name">${name}</span>(`;
                const params = [];
                for (const [key, value] of Object.entries(input)) {
                    if (value !== null && value !== undefined) {
                        // Format different types of values
                        let formattedValue = typeof value === "string" ? `"${value}"` : value;
                        params.push([key, formattedValue]);
                    }
                }

                if (params.length === 0) {
                    return `${result})`;
                } else if (params.length === 1) {
                    // For single parameter, just show the value without the parameter name
                    return `${result}${params[0][1]})`;
                } else {
                    // Format parameters
                    const formattedParams = params.map((p) => `&nbsp;&nbsp;&nbsp;&nbsp;${p[0]}=${p[1]}`).join(",<br/>");
                    return `${result}<br/>${formattedParams}<br/>)`;
                }
            }

            function toggleActionVisibility(element, remainingLines) {
                const container = element.closest(".action-container");
                const preview = container.querySelector(".action-preview");
                const full = container.querySelector(".action-full");

                // Once expanded, keep it expanded
                full.classList.remove("hidden");
                preview.classList.add("hidden");
            }

            function formatToolResultInline(content, targetElement) {
                // Count lines
                const lines = content.split("\n");

                // In compact mode, show only 1 line with [more] link
                if (viewMode === "compact" && lines.length > 1) {
                    // Create container
                    const container = document.createElement("div");

                    // Preview content
                    const previewDiv = document.createElement("div");
                    previewDiv.className = "preview-content";

                    // Add the first line of content plus [more] link
                    const previewContent = lines[0];
                    previewDiv.innerHTML =
                        escapeHtml(previewContent) +
                        ` <span class="more-inline" onclick="toggleResultVisibility(this)">[...]</span>`;

                    // Full content (initially hidden)
                    const contentDiv = document.createElement("pre");
                    contentDiv.className = "hidden";
                    contentDiv.innerHTML = escapeHtml(content);

                    container.appendChild(previewDiv);
                    container.appendChild(contentDiv);
                    targetElement.appendChild(container);
                } else {
                    // For full view or short results, display everything
                    const preElement = document.createElement("pre");
                    preElement.textContent = content;
                    targetElement.appendChild(preElement);
                }
            }

            function toggleResultVisibility(element, remainingLines) {
                const container = element.parentElement.parentElement;
                const preview = container.querySelector(".preview-content");
                const full = container.querySelector("pre");

                // Once expanded, keep it expanded
                full.classList.remove("hidden");
                preview.classList.add("hidden");
            }

            function formatContent(text) {
                return escapeHtml(text);
            }

            function escapeHtml(text) {
                const div = document.createElement("div");
                div.textContent = text;
                return div.innerHTML;
            }

            // Keyboard navigation handler
            document.addEventListener("keydown", function (event) {
                // previous thread
                if ((event.ctrlKey && event.key === "ArrowLeft") || event.key === "h" || event.key === "k") {
                    if (!document.getElementById("prev-thread").disabled) {
                        previousThread();
                    }
                }
                // next thread
                else if ((event.ctrlKey && event.key === "ArrowRight") || event.key === "j" || event.key === "l") {
                    if (!document.getElementById("next-thread").disabled) {
                        nextThread();
                    }
                }
            });

            // Initialize the page
            document.addEventListener("DOMContentLoaded", function () {
                initTheme();
                updateNavigationButtons();
                renderThread();
            });
        </script>
    </body>
</html>
